package com.cneport.das.common.utils;

import com.cneport.das.common.security.pub.DefaultKeyStore;
import com.cneport.das.common.security.pub.SymmetricWithRSAValueEncoder;
import lombok.extern.slf4j.Slf4j;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>标题：DAS 加解密工具类</p>
 * <p>功能：</p>
 * <p>
 * 其他说明：
 * </p>
 * <p>作者：冯继亮</p>
 * <p>审核：</p>
 * <p>重构：</p>
 * <p>创建日期：2021/8/26 周四 15:45</p>
 * 查看帮助：<a href="" target="_blank"></a>
 */
@Slf4j
public class SecurityUtils {

    /**
     * 读取证书开销较大，通过Map缓存，提高效率
     */
    private static final ConcurrentHashMap<String, KeyPair> keyContMap
            = new ConcurrentHashMap<String, KeyPair>();

    /**
     * @param pemUrl
     * @param content
     * @param pri     客户端传 true  使用私钥加解密 服务端传false
     * @return
     */
    public static String encode(String pemUrl, String content, boolean pri) {
        try {
            if (!pemUrl.startsWith("file")) {
                pemUrl = "file:" + pemUrl;
            }
            KeyPair keyPair = getKeyPair(pemUrl);
            SymmetricWithRSAValueEncoder encoder = new SymmetricWithRSAValueEncoder(pri ? keyPair.getPrivate() : keyPair.getPublic());
            byte[] encode = encoder.encode(content.getBytes(StandardCharsets.UTF_8));
            return base64Encode(encode);
        } catch (Exception e) {
            log.error(null, e);
        }
        return "";
    }

    /**
     * @param pemUrl
     * @param encodeStr
     * @param pri       客户端传 true  使用私钥加解密 服务端传false
     * @return
     */
    public static String decode(String pemUrl, String encodeStr, boolean pri) {
        try {
            if (!pemUrl.startsWith("file")) {
                pemUrl = "file:" + pemUrl;
            }
            KeyPair keyPair = getKeyPair(pemUrl);
            byte[] encodeBs = base64Decode(encodeStr);
            SymmetricWithRSAValueEncoder encoderd = new SymmetricWithRSAValueEncoder(pri ? keyPair.getPrivate() : keyPair.getPublic());
            byte[] decode = encoderd.decode(encodeBs);
            return new String(decode, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error(null, e);
        }
        return "";
    }

    /**
     * 返回MD5加密的 16进制字符串
     */
    public static String toMD5Hex(String data) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        MessageDigest messageDigest = MessageDigest.getInstance("MD5");
        messageDigest.reset();
        messageDigest.update(data.getBytes(StandardCharsets.UTF_8));
        return StringUtil.toHexString(messageDigest.digest());
    }


    private static String base64Encode(byte[] bytes) {
        String encoderStr = Base64.getEncoder().encodeToString(bytes);
        //加密后替换
        return encoderStr.replaceAll("\\+", "-").replaceAll("/", "_");
    }

    private static byte[] base64Decode(String str) {
        //解码前替换
        str = str.replaceAll("-", "+").replaceAll("_", "/");
        return Base64.getDecoder().decode(str);
    }

    private static KeyPair getKeyPair(String path) {
        KeyPair jc = keyContMap.get(path);
        if (jc == null) {
            synchronized (path) {
                DefaultKeyStore defaultKeyStore = new DefaultKeyStore();
                defaultKeyStore.load(path, null);
                jc = defaultKeyStore.getKeyPair();
                keyContMap.put(path, jc);
            }
        }
        return jc;
    }

    /**
     * 证书生成
     * KeyPair keyPair = SecurityUtils.generateKey(AccessSecurityConstants.ENCRYPT_TYPE_RSA, 1024);
     * PublicKey publicKey = keyPair.getPublic();
     * PrivateKey privateKey = keyPair.getPrivate();
     * // 私钥
     * String certName =  "test.pem";
     * File file = new File(ResUtils.toURL(sysOptionsService.getACSPEMPath() + certName).toURI());
     * try (FileOutputStream fo = new FileOutputStream(file);)
     * {
     * DefaultKeyStore defaultKeyStore = new DefaultKeyStore(publicKey, privateKey);
     * defaultKeyStore.saveKeyAsPEM(fo);
     * }
     *
     * @param algorithm
     * @param size
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static KeyPair generateKey(String algorithm, int size) throws NoSuchAlgorithmException {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algorithm);// "RSA"
        keyGen.initialize(size);
        KeyPair key = keyGen.generateKeyPair();
        /*
          Private编码： PKCS8 : key.getPrivate().getEncoded()
          Private解码： PKCS8EncodedKeySpec
          Public编码:   X.509 : key.getPublic().getEncoded()
          Public解码：   X509EncodedKeySpec
          String printkey = new String(key.getPublic().getEncoded());
         System.out.print("Generated keypair" + printkey);
        *
        */
        return key;
    }

    static final String[] SecurityPrividers = new String[]{"SunJCE", "BC"};
    static private boolean initSecurityDone;

    public static void addSecurityProvider(String title) {
        if (initSecurityDone) {
            return;
        }
        for (int i = 0; i < SecurityPrividers.length; i++) {
            try {
                final java.security.Provider provider = java.security.Security.getProvider(SecurityPrividers[i]);
                if (provider != null) {
                    initSecurityDone = true;
                    java.security.Security.addProvider(provider);
                } else if ("BC".equals(SecurityPrividers[i])) {
                    java.security.Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
                    initSecurityDone = true;
                }

            } catch (Throwable ex) {
            }
        }
        if (initSecurityDone) {
            return;
        }
        if (log != null) {
            log.error(title, "未能安装 java.security.Security");
        }
    }

}
